#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
执行工具模块

提供重试机制、超时控制、异步执行等工具函数
"""

import time
import asyncio
import functools
from typing import Any, Callable, Optional, Union
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeoutError


def retry_on_error(max_retries: int = 3, delay: float = 1.0, backoff: float = 2.0, 
                   exceptions: tuple = (Exception,)):
    """
    重试装饰器
    
    Args:
        max_retries: 最大重试次数
        delay: 初始延迟时间（秒）
        backoff: 延迟时间倍数
        exceptions: 需要重试的异常类型
    """
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            current_delay = delay
            last_exception = None
            
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_retries:
                        time.sleep(current_delay)
                        current_delay *= backoff
                    else:
                        raise last_exception
            
            return None
        return wrapper
    return decorator


def timeout_decorator(timeout_seconds: float):
    """
    超时装饰器
    
    Args:
        timeout_seconds: 超时时间（秒）
    """
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            with ThreadPoolExecutor(max_workers=1) as executor:
                future = executor.submit(func, *args, **kwargs)
                try:
                    return future.result(timeout=timeout_seconds)
                except FutureTimeoutError:
                    raise TimeoutError(f"Function {func.__name__} timed out after {timeout_seconds} seconds")
        return wrapper
    return decorator


async def run_with_timeout(coro, timeout_seconds: float):
    """
    异步函数超时执行
    
    Args:
        coro: 协程对象
        timeout_seconds: 超时时间（秒）
        
    Returns:
        协程执行结果
        
    Raises:
        asyncio.TimeoutError: 超时异常
    """
    try:
        return await asyncio.wait_for(coro, timeout=timeout_seconds)
    except asyncio.TimeoutError:
        raise TimeoutError(f"Coroutine timed out after {timeout_seconds} seconds")


def rate_limit(calls_per_second: float):
    """
    速率限制装饰器
    
    Args:
        calls_per_second: 每秒允许的调用次数
    """
    min_interval = 1.0 / calls_per_second
    last_called = [0.0]
    
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            elapsed = time.time() - last_called[0]
            left_to_wait = min_interval - elapsed
            if left_to_wait > 0:
                time.sleep(left_to_wait)
            ret = func(*args, **kwargs)
            last_called[0] = time.time()
            return ret
        return wrapper
    return decorator


class CircuitBreaker:
    """
    断路器模式实现
    """
    
    def __init__(self, failure_threshold: int = 5, recovery_timeout: float = 60.0):
        """
        初始化断路器
        
        Args:
            failure_threshold: 失败阈值
            recovery_timeout: 恢复超时时间（秒）
        """
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = 'CLOSED'  # CLOSED, OPEN, HALF_OPEN
    
    def __call__(self, func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if self.state == 'OPEN':
                if time.time() - self.last_failure_time > self.recovery_timeout:
                    self.state = 'HALF_OPEN'
                else:
                    raise Exception("Circuit breaker is OPEN")
            
            try:
                result = func(*args, **kwargs)
                self._on_success()
                return result
            except Exception as e:
                self._on_failure()
                raise e
        
        return wrapper
    
    def _on_success(self):
        """成功时重置计数器"""
        self.failure_count = 0
        self.state = 'CLOSED'
    
    def _on_failure(self):
        """失败时增加计数器"""
        self.failure_count += 1
        self.last_failure_time = time.time()
        
        if self.failure_count >= self.failure_threshold:
            self.state = 'OPEN'


def memoize(maxsize: int = 128):
    """
    记忆化装饰器
    
    Args:
        maxsize: 缓存最大大小
    """
    def decorator(func: Callable) -> Callable:
        cache = {}
        cache_order = []
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 创建缓存键
            key = str(args) + str(sorted(kwargs.items()))
            
            if key in cache:
                # 移动到最前面（LRU）
                cache_order.remove(key)
                cache_order.append(key)
                return cache[key]
            
            # 计算结果
            result = func(*args, **kwargs)
            
            # 添加到缓存
            cache[key] = result
            cache_order.append(key)
            
            # 检查缓存大小
            if len(cache) > maxsize:
                oldest_key = cache_order.pop(0)
                del cache[oldest_key]
            
            return result
        
        # 添加缓存清理方法
        wrapper.cache_clear = lambda: cache.clear() or cache_order.clear()
        wrapper.cache_info = lambda: {'size': len(cache), 'maxsize': maxsize}
        
        return wrapper
    return decorator


def debounce(wait_seconds: float):
    """
    防抖装饰器
    
    Args:
        wait_seconds: 等待时间（秒）
    """
    def decorator(func: Callable) -> Callable:
        last_called = [0.0]
        timer = [None]
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            def call_func():
                last_called[0] = time.time()
                return func(*args, **kwargs)
            
            # 取消之前的定时器
            if timer[0] is not None:
                timer[0].cancel()
            
            # 设置新的定时器
            import threading
            timer[0] = threading.Timer(wait_seconds, call_func)
            timer[0].start()
        
        return wrapper
    return decorator


def throttle(wait_seconds: float):
    """
    节流装饰器
    
    Args:
        wait_seconds: 等待时间（秒）
    """
    def decorator(func: Callable) -> Callable:
        last_called = [0.0]
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            if now - last_called[0] >= wait_seconds:
                last_called[0] = now
                return func(*args, **kwargs)
        
        return wrapper
    return decorator